🧪 QA · Version 1.0

Workflow Lifecycle · Test Scenarios
+ Playwright Automation Guide

Comprehensive test documentation for ConstructQ Workflow v5.0 · 14 test areas · 70 test cases · State diagram SVG · 3 templates · 10 states + 6 triggers · Playwright automation with sample scripts
📅 วันที่: 13 ก.ค. 2569 (2026-07-13) 👤 Owner: วุธ (PM/SA · QA Lead) 🎯 Audience: QA · Dev · Automation 📄 Source: ConstructQ_WorkflowLifecycle_v5.0.html
14
Test Areas
70
Test Cases
10
States · 6 Triggers
3
Seeded Templates

01บทนำ · About This Guide

Test scope · deliverables · references

เอกสารนี้เป็น Test Documentation สำหรับหน้าจอ Workflow Lifecycle v5.0 ครอบคลุม:

  • Test Scenarios · 14 areas — Template list · Create/Edit · Steps · Transitions · SVG diagram · NCR loop
  • Test Cases · 70 cases — Positive / Negative / Boundary / UI / Integration / A11y
  • Playwright Automation Guide — POM · sample scripts · state machine testing
  • NCR Sub-Loop coverage — 3 NCR states + Sub Contractor role + 5 NCR transitions
📌
Reference file: ConstructQ_WorkflowLifecycle_v5.0.html · React SPA · 3 templates seeded · 10 states (7 base + 3 NCR) · 6 triggers · SVG state diagram with cubic bezier arrows · TH/EN toggle

02Screen Overview

Template list · designer · state diagram · 3 templates

🗺️ Screen Map

ViewStateFunction
Template Listview=list3 workflow template cards · Standard · Simple · NCR-Loop
Template PickermodalPick starting template for new workflow
Create/Edit Formview=designerSteps table + Transitions table + SVG State Diagram (live preview)
Save Confirm ModalmodalSummary before publish
Delete ConfirmmodalWarning + Cancel/Confirm

🎨 10 States (7 base + 3 NCR sub-loop)

StateColorTerminalMain RowPurpose
createdBlueNo✅ YesTask created · awaiting assign
assignedPurpleNo✅ YesAssigned to inspector
in_progressGoldNo✅ YesInspection in progress
reviewTealNo✅ YesReviewer approving result
doneGreen✅ Yes✅ YesTask closed
blockedCoralNoNo (below)Paused on blocker
rejectedPinkNoNo (below)Result rejected · returns to assigned
ncr_openRedNoNCR rowDefect found · NCR raised · awaiting Sub
ncr_fixingOrangeNoNCR rowSub is fixing the NCR
ncr_verifyGold-brownNoNCR rowAwaiting QC Inspector verify

⚡ 6 Triggers

TriggerStyleMeaning
autosolid indigoSystem auto-transitions (no user action)
passsolid greenInspector/reviewer marks result as passed
faildashed redInspector/reviewer marks result as failed
blocksolid orangeUser raises blocker (waiting on external)
unblocksolid tealBlocker resolved · resume progress
cancelsolid pinkUser cancels task

📋 3 Seeded Templates

#IconName THStepsTransitionsNCR Loop?
1Workflow มาตรฐาน QC ก่อสร้าง78No
2🪶ตรวจสอบอย่างง่าย (3 ขั้นตอน)32No
3🔧Workflow มาตรฐาน + รอบแก้ NCR1013✅ Yes

🔄 NCR Sub-Loop Transitions (v5.0 new)

FromToTriggerNote
in_progressncr_openfailพบ defect · dashed line going down
ncr_openncr_fixingautoSub รับ NCR · solid purple right
ncr_fixingncr_verifypassSub ส่งการแก้ · solid green right
ncr_verifyncr_fixingfailInspector ไม่ผ่าน · curved back
ncr_verifyin_progresspassInspector อนุมัติ · NCR ปิดอัตโนมัติ · up+left

03Test Strategy

Test types · priority · environments

🎯 Test Types

TypeObjectiveCoverage
PositiveCreate/Edit/Save/Delete workflows~35%
NegativeMissing state · invalid trigger · orphan transition~20%
Boundary0 steps · 100+ steps · single state · loop cycles~10%
UI/UXSVG diagram render · color/style · arrows · animation~15%
IntegrationState machine consistency · task lifecycle enforcement~10%
AccessibilityKeyboard · ARIA · contrast · SVG semantics~10%

⚡ Priority

  • P1 — Critical · save · state machine integrity · NCR sub-loop
  • P2 — High · UI accuracy · role assignment · validation
  • P3 — Medium · language toggle · edge cases · polish

🌐 Environments

  • Local: localStorage-backed · 3 seeded templates as fixtures
  • Staging: Backend + DB · workflow templates table · seed script
  • UAT: Real users test NCR loop end-to-end with actual QC + Sub Contractor

04Test Scenarios + Test Cases

14 areas · 70 detailed test cases

4.1 · 📚 Landing + Template List

TC IDScenario StepsExpected Result PriorityType
TC-01เข้าหน้า Workflow Lifecycle 1. Login (admin_org)
2. Navigate to Master Data → Workflow
AppShell · breadcrumb · title "🔄 Workflow Lifecycle" · 3 template cards P1Pos
TC-023 seeded template cards render 1. View list 3 cards: ⭐ มาตรฐาน · 🪶 อย่างง่าย · 🔧 มาตรฐาน+NCR · แต่ละใบมี name TH+EN · desc · version chip P1Pos
TC-03Status badge "published" visible 1. View card All 3 templates show published badge P2UI
TC-04Click card → open designer 1. Click ⭐ template Designer view opens with template pre-filled P1Pos
TC-05"➕ สร้าง Workflow ใหม่" button 1. Click Create button Template picker modal opens P1Pos

4.2 · 🎯 Template Picker

TC IDScenario StepsExpected Result PriorityType
TC-06Picker shows 3 templates + blank 1. Open picker 4 options: Blank · Standard · Simple · NCR-Loop P1Pos
TC-07Select Standard · Continue 1. Click ⭐ Standard
2. Confirm
Designer opens with 7 steps + 8 transitions pre-loaded P1Pos
TC-08Select NCR-Loop · Continue 1. Click 🔧 NCR-Loop Designer opens with 10 steps + 13 transitions incl. NCR sub-loop P1Pos
TC-09Blank · empty designer 1. Click "เริ่มจากเปล่า" Designer opens · 0 steps · 0 transitions · empty diagram P2Pos
TC-10Cancel picker 1. Click Cancel/backdrop Picker closes · stays on list view P2Pos

4.3 · ➕ Create Template

TC IDScenario StepsExpected Result PriorityType
TC-11Fill name + description 1. Enter name TH/EN + desc Fields accept input · saved to draft P1Pos
TC-12Version default v1.0 1. Leave version empty · Save Defaults to "v1.0" P2Bnd
TC-13Required · name TH empty 1. Skip name · Save Error · block save P1Neg
TC-14Icon selection 1. Choose emoji icon Icon saved with template P3UI
TC-15Circular back icon (v2.0+) 1. View designer header Circular ◀ button before title P3UI

4.4 · ✏️ Edit Existing Template

TC IDScenario StepsExpected Result PriorityType
TC-16Open existing · pre-filled 1. Click ⭐ template Steps + transitions + diagram all pre-loaded P1Pos
TC-17Save updates · same ID 1. Edit name · Save Same ID · updated name in list P1Pos
TC-18Unsaved changes warning 1. Edit · click ◀ back Warning "มีการแก้ไข · ออกจริงไหม?" P2Neg

4.5 · 📋 Steps Editor

TC IDScenario StepsExpected Result PriorityType
TC-19Add step row 1. Click "+ Step" New empty row · auto-id "s-XXXXXX" P1Pos
TC-20State dropdown · 10 options 1. Click state dropdown 10 states: created, assigned, in_progress, review, done, blocked, rejected, ncr_open, ncr_fixing, ncr_verify P1Pos
TC-21Role field · text input 1. Type "QC Inspector" Role saved · role suggestions (QCM, PM, QC Inspector, Sub Contractor) P2Pos
TC-22SLA hours · numeric only 1. Type "abc" Reject non-numeric P2Neg
TC-23Note TH + EN bilingual 1. Enter noteTh + noteEn Both saved · shown per language toggle P2Pos
TC-24Delete step 1. Click delete on row Row removed · related transitions warning if any P1Pos
TC-25Reorder steps · drag/drop 1. Drag s-2 above s-1 Order updates · diagram updates P2Pos
TC-26Duplicate state · warning 1. Add 2 steps with state=created Warning "state ซ้ำ" · but allow (business rule) P3Bnd

4.6 · 🔀 Transitions Editor

TC IDScenario StepsExpected Result PriorityType
TC-27Add transition row 1. Click "+ Transition" New empty row · auto-id "t-XXXXXX" P1Pos
TC-28From/To dropdown · only defined states 1. Open From dropdown Options limited to states defined in Steps table P1Pos
TC-29Trigger dropdown · 6 options 1. Open trigger dropdown auto · pass · fail · block · unblock · cancel P1Pos
TC-30Duplicate transition · same from+to+trigger 1. Add 2 identical transitions Warning "transition ซ้ำ" · block or override P2Neg
TC-31Orphan transition · reference undefined state 1. Add transition with from="foo" (no state) Warning icon · block save P1Neg
TC-32Label TH/EN bilingual 1. Enter labelTh + labelEn Both saved · shown on arrows per language P2Pos
TC-33Delete transition · arrow removed 1. Delete t-1 Row + arrow both removed from diagram P1Pos

4.7 · 🎨 SVG State Diagram

TC IDScenario StepsExpected Result PriorityType
TC-34Diagram renders 7 main states in row 1. Open Standard template 7 state boxes: created→assigned→in_progress→review→done (main row) + blocked+rejected (below) P1UI
TC-35State box color matches WF_STATES 1. Inspect each box created=blue · assigned=purple · done=green · rejected=pink · etc. P2UI
TC-36Arrows · forward (right) 1. Verify created→assigned Solid line · left-to-right · marker at end P1UI
TC-37Arrows · backward (curved) 1. Verify rejected→assigned Curved bezier · goes back P2UI
TC-38Arrows · downward (main → below) 1. Verify in_progress→blocked Arrow goes down P2UI
TC-39Arrow style per trigger 1. Compare pass vs fail arrows pass=solid green · fail=dashed red · auto=solid indigo P2UI
TC-40Arrow label bilingual 1. Toggle TH/EN Labels update (e.g. "อนุมัติ" → "Approve") P2Pos
TC-41Live update · edit step → diagram refreshes 1. Delete a step
2. Verify diagram
State box disappears · related arrows removed P1Int
TC-42Empty state · no steps 1. Blank template Empty diagram placeholder message P3UI

4.8 · 🔄 NCR Sub-Loop Workflow (v5.0)

TC IDScenario StepsExpected Result PriorityType
TC-43NCR template loads · 10 states 1. Open 🔧 NCR-Loop template Steps table has 10 rows including 3 NCR states P1Pos
TC-44NCR states render below main row 1. View diagram NCR row (y=224) below main row · ncr_open, ncr_fixing, ncr_verify visible P1UI
TC-45in_progress → ncr_open (fail down) 1. Verify transition Dashed red arrow going down from in_progress P1UI
TC-46ncr_open → ncr_fixing (auto right) 1. Verify transition Solid purple arrow to the right P1UI
TC-47ncr_fixing → ncr_verify (pass right) 1. Verify transition Solid green arrow right P1UI
TC-48ncr_verify → ncr_fixing (fail curve) 1. Verify transition Dashed red curved arrow back P1UI
TC-49ncr_verify → in_progress (pass up+left) 1. Verify NCR close transition Solid green arrow · goes up + left · closes NCR loop P1UI
TC-50Sub Contractor role · assigned to ncr_fixing 1. View step s-5 Role = "Sub Contractor" P1Pos
TC-51NCR SLA hours · Sub 48h 1. View SLA for ncr_fixing slaHours = 48 P2Pos

4.9 · 👥 Role Assignment

TC IDScenario StepsExpected Result PriorityType
TC-52Role suggestions dropdown 1. Click role field Suggestions: QCM · PM · QC Inspector · Sub Contractor · CEO P2Pos
TC-53Terminal state role = "—" 1. View "done" step Role field = "—" (no role needed after close) P3UI
TC-54Sub Contractor · only in NCR templates 1. Compare Standard vs NCR-Loop Standard has no Sub · NCR-Loop uses Sub Contractor for ncr_fixing P2Pos

4.10 · 💾 Save + Validation

TC IDScenario StepsExpected Result PriorityType
TC-55Save valid workflow · success 1. Complete fields · Save Toast success · redirect to list · new/updated card P1Pos
TC-56Save with 0 steps · blocked 1. Empty steps · Save Error "ต้องมี Step อย่างน้อย 1 รายการ" P1Neg
TC-57Save with no terminal state · warning 1. Remove "done" step · Save Warning "ไม่มี terminal state · task ปิดไม่ได้" P1Neg
TC-58Save with unreachable state · warning 1. Add step no incoming transition · Save Warning "state ไม่มี path เข้าถึง" P2Neg
TC-59Save confirm popup 1. Click Save button Popup summary: name, version, #steps, #transitions P1Pos

4.11 · 🗑️ Delete + Publish Status

TC IDScenario StepsExpected Result PriorityType
TC-60Delete confirm dialog 1. Delete workflow Modal warning "ลบ workflow? · Task ที่ใช้จะถูกกระทบ" P1Pos
TC-61Confirm delete · removed 1. Confirm Workflow หายจาก list · toast success P1Pos
TC-62Publish · status = published 1. Click "เผยแพร่" Status changes to "published" · badge updates P1Pos
TC-63Draft mode · not selectable in Task Mgmt 1. Create draft · check Task Mgmt Only published workflows available P2Int

4.12 · 🌐 Language Toggle TH/EN (v3.0)

TC IDScenario StepsExpected Result PriorityType
TC-64Toggle TH → EN · React page updates 1. Click EN flag State names · labels · steps · transitions text switch to EN P1Pos
TC-65Data preserved on toggle 1. Fill · toggle · toggle back Data intact P1Pos

4.13 · 💾 Data Persistence

TC IDScenario StepsExpected Result PriorityType
TC-66Refresh · 3 seed persist 1. F5 3 seeded templates still present P1Int
TC-67Custom workflow persists 1. Create custom · reload Custom still in list P1Int
TC-68Clear localStorage · fallback to 3 seed 1. Clear storage · reload Reverts to 3 seed templates P3Int

4.14 · ♿ Accessibility

TC IDScenario StepsExpected Result PriorityType
TC-69Keyboard nav · full designer 1. Tab through steps → transitions → save Focus visible · logical order P2A11y
TC-70SVG diagram · ARIA + contrast 1. Screen reader on SVG · axe scan State names announced · arrows have title · contrast ≥ 4.5:1 P2A11y
📊
Test Case Summary: รวม 70 test cases · P1 = 32 · P2 = 28 · P3 = 10 · Positive 27 · Negative 8 · Boundary 3 · Integration 5 · UI 21 · A11y 2

05Playwright Automation Guide + Sample Scripts

Setup · POM · sample tests for state machine · NCR loop

🚀 Setup

  1. Install: npm init playwright@latest
  2. Fixture reset: Seed 3 templates before each test
  3. SVG testing: Query SVG elements via page.locator('svg [data-state="ncr_open"]')
  4. State machine assertions: Verify counts + relationships + arrow styles

📁 Project Structure

treetests/workflow/
├── list.spec.ts               # TC-01 to TC-05
├── template-picker.spec.ts    # TC-06 to TC-10
├── create.spec.ts             # TC-11 to TC-15
├── edit.spec.ts               # TC-16 to TC-18
├── steps-editor.spec.ts       # TC-19 to TC-26
├── transitions.spec.ts        # TC-27 to TC-33
├── svg-diagram.spec.ts        # TC-34 to TC-42
├── ncr-loop.spec.ts           # TC-43 to TC-51 (CRITICAL)
├── role-assign.spec.ts        # TC-52 to TC-54
├── save-validation.spec.ts    # TC-55 to TC-59
├── delete-publish.spec.ts     # TC-60 to TC-63
├── language.spec.ts           # TC-64, TC-65
├── persistence.spec.ts        # TC-66 to TC-68
├── a11y.spec.ts               # TC-69, TC-70
└── e2e-full-flow.spec.ts      # Composite smoke
pages/
├── WorkflowListPage.ts
├── WorkflowDesignerPage.ts
├── StepsEditor.ts
├── TransitionsEditor.ts
└── SVGDiagram.ts
fixtures/
└── seed-workflows.json
playwright.config.ts

🎯 Page Object Model · WorkflowDesignerPage

TypeScriptimport { Page, Locator, expect } from '@playwright/test';

export type StateId = 'created' | 'assigned' | 'in_progress' | 'review' | 'done'
                     | 'blocked' | 'rejected' | 'ncr_open' | 'ncr_fixing' | 'ncr_verify';
export type Trigger = 'auto' | 'pass' | 'fail' | 'block' | 'unblock' | 'cancel';

export class WorkflowDesignerPage {
  readonly page: Page;
  readonly nameTh: Locator;
  readonly nameEn: Locator;
  readonly version: Locator;
  readonly btnAddStep: Locator;
  readonly btnAddTransition: Locator;
  readonly btnSave: Locator;
  readonly stepRows: Locator;
  readonly transitionRows: Locator;
  readonly svgDiagram: Locator;

  constructor(page: Page) {
    this.page = page;
    this.nameTh = page.locator('#wfNameTh');
    this.nameEn = page.locator('#wfNameEn');
    this.version = page.locator('#wfVersion');
    this.btnAddStep = page.getByRole('button', { name: /\+ Step/ });
    this.btnAddTransition = page.getByRole('button', { name: /\+ Transition/ });
    this.btnSave = page.getByRole('button', { name: /บันทึก|Save/ });
    this.stepRows = page.locator('.wf-step-row');
    this.transitionRows = page.locator('.wf-trans-row');
    this.svgDiagram = page.locator('svg.wf-diagram');
  }

  async addStep(state: StateId, role: string, slaHours: number = 4, note?: string) {
    await this.btnAddStep.click();
    const last = this.stepRows.last();
    await last.locator('.wf-state-select').selectOption(state);
    await last.locator('.wf-role-input').fill(role);
    await last.locator('.wf-sla-input').fill(String(slaHours));
    if (note) await last.locator('.wf-note-input').fill(note);
  }

  async addTransition(from: StateId, to: StateId, trigger: Trigger, label?: string) {
    await this.btnAddTransition.click();
    const last = this.transitionRows.last();
    await last.locator('.wf-from-select').selectOption(from);
    await last.locator('.wf-to-select').selectOption(to);
    await last.locator('.wf-trigger-select').selectOption(trigger);
    if (label) await last.locator('.wf-label-input').fill(label);
  }

  async save() {
    await this.btnSave.click();
    await this.page.getByRole('dialog').waitFor({ state: 'visible' });
    await this.page.getByRole('button', { name: /ยืนยัน|Confirm/ }).click();
  }

  // SVG diagram helpers
  async stateBox(state: StateId): Promise<Locator> {
    return this.svgDiagram.locator(`[data-state="${state}"]`);
  }

  async arrowExists(from: StateId, to: StateId, trigger: Trigger): Promise<boolean> {
    const arrow = this.svgDiagram.locator(
      `[data-from="${from}"][data-to="${to}"][data-trigger="${trigger}"]`
    );
    return (await arrow.count()) > 0;
  }
}

✅ Sample Test 1 · Standard Template Creation

TypeScriptimport { test, expect } from '@playwright/test';
import { WorkflowDesignerPage } from '../pages/WorkflowDesignerPage';

test.describe('Workflow · Standard Template', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/master-data/workflow');
  });

  test('TC-02 · 3 seeded templates render', async ({ page }) => {
    const cards = page.locator('.wf-card');
    await expect(cards).toHaveCount(3);
    await expect(page.getByText('Workflow มาตรฐาน QC')).toBeVisible();
    await expect(page.getByText('ตรวจสอบอย่างง่าย')).toBeVisible();
    await expect(page.getByText('Workflow มาตรฐาน + รอบแก้ NCR')).toBeVisible();
  });

  test('TC-07 · Create Standard workflow from scratch', async ({ page }) => {
    await page.getByRole('button', { name: /สร้าง Workflow ใหม่/ }).click();
    await page.getByText('⭐ Standard').click();
    await page.getByRole('button', { name: /เริ่มต้น|Continue/ }).click();

    const designer = new WorkflowDesignerPage(page);
    await expect(designer.stepRows).toHaveCount(7);        // 7 base states
    await expect(designer.transitionRows).toHaveCount(8);  // 8 base transitions
  });
});

✅ Sample Test 2 · NCR Sub-Loop (CRITICAL v5.0)

TypeScripttest.describe('Workflow · NCR Sub-Loop v5.0', () => {
  test('TC-43 · NCR template loads · 10 states', async ({ page }) => {
    await page.goto('/master-data/workflow');
    await page.getByText('Workflow มาตรฐาน + รอบแก้ NCR').click();

    const designer = new WorkflowDesignerPage(page);
    await expect(designer.stepRows).toHaveCount(10);

    // Verify all 3 NCR states present
    for (const state of ['ncr_open', 'ncr_fixing', 'ncr_verify'] as const) {
      const box = await designer.stateBox(state);
      await expect(box).toBeVisible();
    }
  });

  test('TC-45 → TC-49 · All 5 NCR transitions render correctly', async ({ page }) => {
    await page.goto('/master-data/workflow');
    await page.getByText('Workflow มาตรฐาน + รอบแก้ NCR').click();

    const designer = new WorkflowDesignerPage(page);

    // 1. in_progress → ncr_open (fail down)
    expect(await designer.arrowExists('in_progress', 'ncr_open', 'fail')).toBe(true);

    // 2. ncr_open → ncr_fixing (auto right)
    expect(await designer.arrowExists('ncr_open', 'ncr_fixing', 'auto')).toBe(true);

    // 3. ncr_fixing → ncr_verify (pass right)
    expect(await designer.arrowExists('ncr_fixing', 'ncr_verify', 'pass')).toBe(true);

    // 4. ncr_verify → ncr_fixing (fail curve back)
    expect(await designer.arrowExists('ncr_verify', 'ncr_fixing', 'fail')).toBe(true);

    // 5. ncr_verify → in_progress (pass up+left · NCR close)
    expect(await designer.arrowExists('ncr_verify', 'in_progress', 'pass')).toBe(true);
  });

  test('TC-50 · Sub Contractor role assigned to ncr_fixing', async ({ page }) => {
    await page.goto('/master-data/workflow');
    await page.getByText('Workflow มาตรฐาน + รอบแก้ NCR').click();

    const ncrFixingRow = page.locator('.wf-step-row[data-state="ncr_fixing"]');
    const roleInput = ncrFixingRow.locator('.wf-role-input');
    await expect(roleInput).toHaveValue('Sub Contractor');

    const slaInput = ncrFixingRow.locator('.wf-sla-input');
    await expect(slaInput).toHaveValue('48');
  });
});

✅ Sample Test 3 · Build Custom Workflow End-to-End

TypeScripttest('TC-E2E-01 · Build custom NCR workflow from scratch', async ({ page }) => {
  const designer = new WorkflowDesignerPage(page);

  // Open blank designer
  await page.goto('/master-data/workflow');
  await page.getByRole('button', { name: /สร้าง Workflow ใหม่/ }).click();
  await page.getByText('เริ่มจากเปล่า').click();

  // Fill basic info
  await designer.nameTh.fill('Workflow ทดสอบ NCR');
  await designer.nameEn.fill('Test NCR Workflow');
  await designer.version.fill('v1.0');

  // Add 5 main states + 3 NCR states
  await designer.addStep('created',     'QCM',             4);
  await designer.addStep('assigned',    'QCM',             8);
  await designer.addStep('in_progress', 'QC Inspector',    24);
  await designer.addStep('ncr_open',    'QC Inspector',    2);
  await designer.addStep('ncr_fixing',  'Sub Contractor',  48);
  await designer.addStep('ncr_verify',  'QC Inspector',    4);
  await designer.addStep('done',        '—',               0);

  // Add all NCR transitions
  await designer.addTransition('created',     'assigned',    'auto');
  await designer.addTransition('assigned',    'in_progress', 'auto');
  await designer.addTransition('in_progress', 'ncr_open',    'fail');   // NCR trigger
  await designer.addTransition('ncr_open',    'ncr_fixing',  'auto');
  await designer.addTransition('ncr_fixing',  'ncr_verify',  'pass');
  await designer.addTransition('ncr_verify',  'ncr_fixing',  'fail');   // re-fix
  await designer.addTransition('ncr_verify',  'in_progress', 'pass');   // NCR close
  await designer.addTransition('in_progress', 'done',        'pass');

  // Save with confirm popup
  await designer.save();

  // Verify list shows 4 templates now (3 seed + 1 new)
  await expect(page.locator('.wf-card')).toHaveCount(4);
  await expect(page.getByText('Workflow ทดสอบ NCR')).toBeVisible();
});

✅ Sample Test 4 · SVG Diagram Arrow Style Verification

TypeScripttest('TC-39 · Arrow style per trigger', async ({ page }) => {
  await page.goto('/master-data/workflow');
  await page.getByText('Workflow มาตรฐาน QC').click();

  // Verify pass arrow · solid green
  const passArrow = page.locator('svg [data-from="review"][data-to="done"][data-trigger="pass"]');
  await expect(passArrow).toHaveAttribute('stroke', '#27AE60');
  await expect(passArrow).not.toHaveAttribute('stroke-dasharray', /.+/);  // no dash

  // Verify fail arrow · dashed red
  const failArrow = page.locator('svg [data-from="review"][data-to="rejected"][data-trigger="fail"]');
  await expect(failArrow).toHaveAttribute('stroke', /#DC2626|#FF5A7A/);
  await expect(failArrow).toHaveAttribute('stroke-dasharray', /.+/);  // has dash

  // Verify auto arrow · solid indigo
  const autoArrow = page.locator('svg [data-trigger="auto"]').first();
  await expect(autoArrow).not.toHaveAttribute('stroke-dasharray', /.+/);
});

✅ Sample Test 5 · Language Toggle (v3.0)

TypeScripttest('TC-64 · Language TH → EN updates React page content', async ({ page }) => {
  await page.goto('/master-data/workflow');

  // Default TH · check Thai text
  await expect(page.getByText('Workflow มาตรฐาน QC ก่อสร้าง')).toBeVisible();

  // Toggle to EN
  await page.locator('[data-lang="en"]').click();

  // Verify EN content
  await expect(page.getByText('Standard Construction QC Workflow')).toBeVisible();

  // Open template · verify state labels also switch
  await page.getByText('Standard Construction QC Workflow').click();
  await expect(page.getByText('Assigned')).toBeVisible();   // state name EN
  await expect(page.getByText('In Progress')).toBeVisible();
});

🎭 Fixture · Reset localStorage Before Each Test

TypeScript// tests/setup.ts
import seed from '../fixtures/seed-workflows.json';

test.beforeEach(async ({ page }) => {
  await page.addInitScript((data) => {
    localStorage.setItem('wf_templates', JSON.stringify(data));
  }, seed);
});
💡
Best Practices: Use data-state · data-from · data-to · data-trigger attributes on SVG for reliable selectors · verify state machine consistency via counts (7 states → 8 transitions typical) · always test NCR loop via all 5 transitions · confirm popup as checkpoint

06Appendix

📌 Version History

  • ConstructQ_WorkflowLifecycle_v5.0.html · source · NCR sub-loop diagram drawing complete
  • เอกสารนี้ v1.0 · 13 ก.ค. 2569 · 70 test cases · Playwright + sample scripts

🔗 References

  • Playwright Docs: https://playwright.dev
  • State machine testing: https://xstate.js.org/docs/
  • Related file: ConstructQ_WorkflowLifecycle_ER_v2.0.html · Tables_v2.0.xlsx
  • Excel companion: ConstructQ_Workflow_TestScenario_v1.0.xlsx

🌳 3 Seeded Templates Reference

IDIconStepsTransitionsNCR Loop
wf-tpl-qc-standard78No
wf-tpl-simple🪶32No
wf-tpl-ncr-loop🔧1013✅ Yes